package cs757;

import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;

public class EncryptionHandler {
	private Cipher threeDESEncrypter;
	private Cipher threeDESDecrypter;
	
	/**
	 * Builds the classes necessary to triple-DES encryption and decryption.
	 * @param key - The DES key to use from encryption and decryption, should be 64 bits, hexadecimal
	 * @throws DecoderException 
	 * @throws InvalidKeyException 
	 * @throws NoSuchAlgorithmException 
	 * @throws InvalidKeySpecException 
	 * @throws NoSuchPaddingException 
	 * @throws InvalidAlgorithmParameterException 
	 */
	public EncryptionHandler(String key) throws DecoderException, InvalidKeyException, InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException{
		//Convert the given key in to something that can be used by DES
		byte[] keyBytes = Hex.decodeHex(key.toCharArray());
			
		//Build a cipher suite out of the key
		DESedeKeySpec secretKeySpec = new DESedeKeySpec(keyBytes);
		IvParameterSpec iv = new IvParameterSpec(new byte[8]);
			
		SecretKey secretKey = SecretKeyFactory.getInstance("DESede").generateSecret(secretKeySpec);
			
	    threeDESEncrypter = Cipher.getInstance("DESede/CBC/PKCS5Padding");
	    threeDESEncrypter.init(Cipher.ENCRYPT_MODE, secretKey, iv);
	    	
	    threeDESDecrypter = Cipher.getInstance("DESede/CBC/PKCS5Padding");
	    threeDESDecrypter.init(Cipher.DECRYPT_MODE, secretKey, iv);
	}
	
	/**
	 * Encodes the given integer using triple-DES
	 * @param toEncode - The integer to encrypt
	 * @return A string representing the encrypted integer, hexadecimally encoded
	 * @throws BadPaddingException 
	 * @throws IllegalBlockSizeException 
	 */
	public String encodeInt(int toEncode) throws IllegalBlockSizeException, BadPaddingException {
		byte[] byteArray = Integer.toHexString(toEncode).getBytes();
		
		byte[] cipherText = threeDESEncrypter.doFinal(byteArray);
		return new String(Hex.encodeHex(cipherText));
	}
	
	/**
	 * Decrypts an integer from the given cipher text using triple-DES
	 * @param cipherText - The ciphertext (hexadecimally encoded) to decrypt
	 * @return The integer contained in the given ciphertext
	 * @throws DecoderException 
	 * @throws BadPaddingException 
	 * @throws IllegalBlockSizeException 
	 */
	public int decodeInt(String cipherText) throws IllegalBlockSizeException, BadPaddingException, DecoderException {
		byte[] plainText = threeDESDecrypter.doFinal(Hex.decodeHex(cipherText.toCharArray()));
		return Integer.parseInt(new String(plainText), 16);
	}
}
